

package SSF.OS;

import SSF.Net.Util.*;

/** ICMP packet Header class.
 */
public class ICMPHeader extends ProtocolMessage {

    //----------------------------------------------------------------------
    // FACTORY METHODS 

    public static ICMPHeader makeEchoRequestMessage(int id, int seq, int len, 
						    int destAddr){
	ICMPHeader ret = new ICMPHeader(ICMP.ICMP_ECHO_REQUEST,0,destAddr);
	ret.id = id;
	ret.seq = seq;
	ret.datalen = len;
	return ret;
    }

    public static ICMPHeader makeEchoResponseMessage(ICMPHeader request) {
	if (ICMP.ICMP_ECHO_REQUEST!=request.icmpType) {
	    System.err.println("Cannot make echo response from "+request);
	    return null;
	}
	int destAddr = request.ipHeader().SOURCE_IP;
	ICMPHeader ret = new ICMPHeader(ICMP.ICMP_ECHO_RESPONSE,0,destAddr);
	ret.id = request.id;
	ret.seq = request.seq;
	ret.datalen = request.datalen;
	return ret;
    }

    public static ICMPHeader makeTimestampRequestMessage(int id, int seq, 
							 int destAddr, 
							 long now){
	ICMPHeader ret = 
	    new ICMPHeader(ICMP.ICMP_TIMESTAMP_REQUEST,0,destAddr);
	ret.id = id;
	ret.seq = seq;
	ret.originTimestamp = (int)(now*100./SSF.Net.Net.frequency);
	return ret;
    }

    public static ICMPHeader makeTimestampResponseMessage(ICMPHeader request,
							  long now) {
	if (ICMP.ICMP_TIMESTAMP_REQUEST!=request.icmpType) {
	    System.err.println("Cannot make tstamp response from "+request);
	    return null;
	}
	int destAddr = request.ipHeader().SOURCE_IP;
	ICMPHeader ret = 
	    new ICMPHeader(ICMP.ICMP_TIMESTAMP_RESPONSE,0,destAddr);
	ret.id = request.id;
	ret.seq = request.seq;
	ret.receiveTimestamp = // STUB -- for us, always the same.
	    ret.transmitTimestamp = 
	    (int)(now*100./SSF.Net.Net.frequency); 
	
	return ret;
    }

    private static ICMPHeader makeFailureMessage(IpHeader problem,
						 int mtype, int mcode) {
	ICMPHeader ret = new ICMPHeader(mtype,mcode,problem.SOURCE_IP);
	ret.failedHeader = problem;
	return ret;
    }

    public static ICMPHeader makeHostUnreachableMessage(IpHeader problem) {
	return makeFailureMessage(problem,
				  ICMP.ICMP_DESTINATION_UNREACHABLE,
				  ICMP.ICMP_CODE_HOST_UNREACHABLE);
    }

    public static ICMPHeader makeProtocolUnreachableMessage(IpHeader problem) {
	return makeFailureMessage(problem,
				  ICMP.ICMP_DESTINATION_UNREACHABLE,
				  ICMP.ICMP_CODE_PROTOCOL_UNREACHABLE);
    }

    public static ICMPHeader makePortUnreachableMessage(IpHeader problem) {
	return makeFailureMessage(problem,
				  ICMP.ICMP_DESTINATION_UNREACHABLE,
				  ICMP.ICMP_CODE_PORT_UNREACHABLE);
    }

    public static ICMPHeader makeTimeExceededMessage(IpHeader problem) {
	return makeFailureMessage(problem,ICMP.ICMP_TIME_EXCEEDED, 0);				
    }


    //----------------------------------------------------------------------

    public String toString() {
	switch(icmpType) {
	case ICMP.ICMP_ECHO_REQUEST:
	    return "{REQUEST "+datalen+" bytes ("+id+"/"+seq+
		") from "+IP_s.IPtoString(ipHeader().SOURCE_IP)+"}";
	case ICMP.ICMP_ECHO_RESPONSE:
	    return "{RESPONSE "+datalen+" bytes ("+id+"/"+seq+
		") from "+IP_s.IPtoString(ipHeader().SOURCE_IP)+"}";
	case ICMP.ICMP_DESTINATION_UNREACHABLE:
	    switch(icmpCode) {
	    case ICMP.ICMP_CODE_HOST_UNREACHABLE: 
		return "{Host unreachable: "+
		    IP_s.IPtoString(failedHeader.DEST_IP)+": msg from "+
		    IP_s.IPtoString(ipHeader().SOURCE_IP)+"}";
	    case ICMP.ICMP_CODE_PROTOCOL_UNREACHABLE: 
		return "{Protocol unreachable: "+
		    Protocols.getProtocolByNumber(failedHeader.PROTOCOL_NO)+
		    "("+failedHeader.PROTOCOL_NO+") on "+
		    IP_s.IPtoString(failedHeader.DEST_IP)+": msg from "+
		    IP_s.IPtoString(ipHeader().SOURCE_IP)+"}";
	    case ICMP.ICMP_CODE_PORT_UNREACHABLE: 
		return "{Port unreachable: protocol "+
		    Protocols.getProtocolByNumber(failedHeader.PROTOCOL_NO)+
		    "("+failedHeader.PROTOCOL_NO+") on "+
		    IP_s.IPtoString(failedHeader.DEST_IP)+": msg from "+
		    IP_s.IPtoString(ipHeader().SOURCE_IP)+"}";
	    default:
		return "{Destination unreachable, unsupported code "+
		    icmpCode+"}";
	    }
	case ICMP.ICMP_TIME_EXCEEDED:
	    return "{TIME_EXCEEDED from "+
		IP_s.IPtoString(ipHeader().SOURCE_IP)+"}";
	default:
	    return "{?ICMP}";
	}
    }

    //----------------------------------------------------------------------
    // CORE DATA, PRIVATE CONSTRUCTOR (USE FACTORY METHODS) 

    public IpHeader failedHeader = null; // enabled for unreachable msgs

    public int icmpType,icmpCode;

    public int id,seq,datalen;

    /** These timestamps are in hundredths of seconds. */
    public int originTimestamp,receiveTimestamp,transmitTimestamp;

    public IpHeader ipHeader() {
	return ((IpHeader)previous());
    }
    
    private ICMPHeader(int icmpType, int icmpCode, int destAddr) {
	this.icmpType = icmpType;
	this.icmpCode = icmpCode;
	IpHeader iphdr = new IpHeader(Protocols.ICMP_PRTL_NUM,-1,destAddr); 
	iphdr.carryPayload(this);
    }

    /** Returns nominal size (in bytes) of the ICMP header only.
     *  Includes the size of the IP header of packet that caused
     *  the error, for DESTINATION_UNREACHABLE and TIME_EXCEEDED,
     *  plus first eight bytes of the IP packet's payload.
     */
    public int header_bytecount(){
	switch(icmpType) {
	case ICMP.ICMP_ECHO_REQUEST:
	case ICMP.ICMP_ECHO_RESPONSE:
	    return 8+datalen;
	case ICMP.ICMP_DESTINATION_UNREACHABLE:
	case ICMP.ICMP_TIME_EXCEEDED:
	    return 8+failedHeader.header_bytecount()+8;
	default:
	    System.err.println("** ICMPHeader.header_bytecount() "+
			       "unknown message type: "+this);
	    return 8;
	}
    }
    
    /** Returns the total number of bytes in this ICMP packet.
     */
    public int bytecount() {
	ProtocolMessage tail = payload();
	return (header_bytecount() + (tail==null?0:tail.bytecount()));
    }
}


/*=                                                                      =*/
/*=  Copyright (c) 1997--2000  SSF Research Network                      =*/
/*=                                                                      =*/
/*=  SSFNet is open source software, distributed under the GNU General   =*/
/*=  Public License.  See the file COPYING in the 'doc' subdirectory of  =*/
/*=  the SSFNet distribution, or http://www.fsf.org/copyleft/gpl.html    =*/
/*=                                                                      =*/
